/*
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*         http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

#include <gtest/gtest.h>
#include <test_utilities/test_base.h>
#include "interface/network_port/link_info.h"
#include <ncsi_over_mctp/ncsi_over_mctp_mellanox.h>
#include <mctp/mctp.h>
#include <mc/engine.h>
#include <mc/common.h>
#include <ncsi/ncsi_protocol.h>
#include <ncsi/ncsi_socket.h>
#include "mock_ipmi_core.h"
#include <ctime>
#include <thread>
#include <chrono>
#include <functional>
#include <vector>
#include <string>

// 声明外部 MCTP 请求处理器设置函数
namespace dev {
void set_mctp_request_handler(std::function<bool(const mc::dict&, const std::vector<uint8_t>&,
                                                 std::string&)> handler);
}

namespace drivers {
namespace pcie_nic_card {
namespace mellanox {
namespace interface {
namespace test {

namespace {
// 字节序转换辅助函数
constexpr uint16_t host_to_be16(uint16_t value) {
    return static_cast<uint16_t>(((value & 0x00FFU) << 8U) | ((value & 0xFF00U) >> 8U));
}

// 辅助函数：创建 GET_LINK_STATUS 响应
std::string make_get_link_status_response(uint8_t link_flag = 0x01, uint8_t speed_duplex = 0x08) {
    using namespace dev;
    link_status_rsp response{};
    response.rsp_code             = host_to_be16(COMMAND_COMPLETED);
    response.reason_code          = host_to_be16(NOERROR_OR_NOREASON);
    response.lnk_sts.link_flag    = link_flag;
    response.lnk_sts.speed_duplex = speed_duplex;
    std::string result(reinterpret_cast<const char*>(&response), GET_LINK_STATUS_RSP_LEN);
    return result;
}
} // namespace

class link_info_test_service : public mc::engine::service {
public:
    link_info_test_service(const std::string& name) : mc::engine::service(name) {}
};

// ========================= NetworkPort_LinkInfo测试 =========================
class NetworkPortLinkInfoTest : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service = std::make_unique<link_info_test_service>("bmc.openubmc.test_link_info");
        m_test_service->init();
        m_test_service->start();
        
        // 注册 mock IpmiCore 接口，避免真实的 D-Bus 调用
        m_ipmi_core_obj = test_ipmi_core_object::create();
        m_ipmi_core_obj->set_object_path("/bmc/kepler/IpmiCore");
        m_ipmi_core_obj->set_object_name("bmc.kepler.IpmiCore");
        m_test_service->register_object(m_ipmi_core_obj);
    }
    static void TearDownTestSuite() {
        try {
            if (m_ipmi_core_obj && m_test_service) {
                m_test_service->unregister_object(m_ipmi_core_obj);
                m_ipmi_core_obj.reset();
            }
            if (m_test_service) {
                m_test_service->stop();
                m_test_service.reset();
            }
            TestWithEngine::TearDownTestSuite();
        } catch (...) {
            if (m_ipmi_core_obj) {
                m_ipmi_core_obj.reset();
            }
            if (m_test_service) {
                m_test_service.reset();
            }
        }
    }
    void SetUp() override {
        m_link_info = mc::make_shared<dev::NetworkPort_LinkInfo>();

        // 创建真实的 ncsi_over_mctp_mellanox，通过 MCTP 请求处理器来模拟响应
        m_mctp = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE_NCSI, "");
        m_ncsi = std::make_shared<dev::ncsi_over_mctp_mellanox>(*m_mctp);
    }
    void TearDown() override {
        try {
            if (m_link_info) {
                m_link_info->stop_ncsi_update_task();
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                m_link_info.reset();
            }
            // 清理 MCTP 请求处理器，避免影响其他测试
            dev::set_mctp_request_handler(nullptr);
            m_ncsi.reset();
            m_mctp.reset();
        } catch (...) {
            if (m_link_info) {
                m_link_info.reset();
            }
        }
    }

    static std::unique_ptr<link_info_test_service> m_test_service;
    static mc::shared_ptr<test_ipmi_core_object> m_ipmi_core_obj;
    mc::shared_ptr<dev::NetworkPort_LinkInfo>      m_link_info;
    mc::shared_ptr<dev::mctp>                     m_mctp;
    std::shared_ptr<dev::ncsi_over_mctp_mellanox> m_ncsi;
};
std::unique_ptr<link_info_test_service> NetworkPortLinkInfoTest::m_test_service;
mc::shared_ptr<test_ipmi_core_object> NetworkPortLinkInfoTest::m_ipmi_core_obj;

// ========================= 基本功能测试 =========================

TEST_F(NetworkPortLinkInfoTest, Constructor) {
    dev::NetworkPort_LinkInfo link_info;
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, Destructor_CleansUpResources) {
    auto link_info = mc::make_shared<dev::NetworkPort_LinkInfo>();

    // 启动任务后析构
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;
    link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    link_info->stop_ncsi_update_task();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    link_info.reset();

    EXPECT_TRUE(link_info == nullptr);
}

// ========================= update_link_status_by_ncsi 测试 =========================

TEST_F(NetworkPortLinkInfoTest, UpdateLinkStatusByNcsi_Success) {
    // 设置 MCTP 请求处理器，返回模拟的链路状态信息
    dev::set_mctp_request_handler([](const mc::dict& ctx, const std::vector<uint8_t>& data,
                                     std::string& rsp) {
        EXPECT_EQ(ctx.at("PacketType").as<int64_t>(), GET_LINK_STATUS);
        EXPECT_TRUE(data.empty());
        // link_flag=1 (UP), speed_duplex=8 (10000Mbps, full duplex)
        rsp = make_get_link_status_response(0x01, 0x08);
        return true;
    });

    // 需要先设置 ncsi_over_mctp_mellanox
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    bool result = m_link_info->update_link_status_by_ncsi(port_id);
    EXPECT_TRUE(result);
    EXPECT_EQ(m_link_info->LinkStatus.get_value(), 1);
    EXPECT_EQ(m_link_info->SpeedMbps.get_value(), 10000);
    EXPECT_EQ(m_link_info->FullDuplex.get_value(), true);
}

TEST_F(NetworkPortLinkInfoTest, UpdateLinkStatusByNcsi_GetLinkStatusFailed) {
    // 设置 MCTP 请求处理器返回失败
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&, std::string&) {
        return false;
    });

    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    bool result = m_link_info->update_link_status_by_ncsi(port_id);
    EXPECT_FALSE(result);
    EXPECT_EQ(m_link_info->LinkStatus.get_value(), 255);  // 无效值
    EXPECT_EQ(m_link_info->SpeedMbps.get_value(), 0);
    EXPECT_EQ(m_link_info->FullDuplex.get_value(), false);
}

TEST_F(NetworkPortLinkInfoTest, UpdateLinkStatusByNcsi_LinkDown) {
    // 设置 MCTP 请求处理器，返回链路断开状态
    dev::set_mctp_request_handler([](const mc::dict& ctx, const std::vector<uint8_t>& data,
                                     std::string& rsp) {
        EXPECT_EQ(ctx.at("PacketType").as<int64_t>(), GET_LINK_STATUS);
        EXPECT_TRUE(data.empty());
        // link_flag=0 (DOWN)
        rsp = make_get_link_status_response(0x00, 0x00);
        return true;
    });

    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    bool result = m_link_info->update_link_status_by_ncsi(port_id);
    EXPECT_TRUE(result);
    EXPECT_EQ(m_link_info->LinkStatus.get_value(), 0);
    EXPECT_EQ(m_link_info->SpeedMbps.get_value(), 0);
    EXPECT_EQ(m_link_info->FullDuplex.get_value(), false);
}

TEST_F(NetworkPortLinkInfoTest, UpdateLinkStatusByNcsi_DifferentSpeeds) {
    // speed_duplex 映射: 3=100Mbps, 6=1000Mbps, 8=10000Mbps, 9=20000Mbps, 10=25000Mbps, 11=40000Mbps, 12=50000Mbps, 13=100000Mbps
    std::vector<std::pair<uint32_t, uint8_t>> speed_map = {
        {100, 3}, {1000, 6}, {10000, 8}, {20000, 9}, {25000, 10}, {40000, 11}, {50000, 12}, {100000, 13}};
    uint8_t port_id    = 0;
    uint8_t package_id = 0;

    for (auto [speed, speed_duplex] : speed_map) {
        // 设置 MCTP 请求处理器，返回不同速度的链路状态
        dev::set_mctp_request_handler([speed_duplex](const mc::dict& ctx, const std::vector<uint8_t>& data,
                                                     std::string& rsp) {
            EXPECT_EQ(ctx.at("PacketType").as<int64_t>(), GET_LINK_STATUS);
            EXPECT_TRUE(data.empty());
            rsp = make_get_link_status_response(0x01, speed_duplex);
            return true;
        });

        mc::milliseconds interval(1000);
        m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));

        bool result = m_link_info->update_link_status_by_ncsi(port_id);
        EXPECT_TRUE(result);
        EXPECT_EQ(m_link_info->SpeedMbps.get_value(), speed);
        m_link_info->stop_ncsi_update_task();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

TEST_F(NetworkPortLinkInfoTest, UpdateLinkStatusByNcsi_WithoutNcsiInitialized) {
    // 不启动任务，直接调用 update_link_status_by_ncsi
    uint8_t port_id = 0;
    bool    result  = m_link_info->update_link_status_by_ncsi(port_id);
    // 由于 m_ncsi_over_mctp_mellanox 为 nullptr，应该会失败
    EXPECT_FALSE(result);
    // 注意：当 NCSI 未初始化时，LinkStatus 不会被设置为 255，因为函数直接返回 false
    // 所以这里不检查 LinkStatus 的值
}

// ========================= start_ncsi_update_task_mellanox 测试 =========================

TEST_F(NetworkPortLinkInfoTest, StartNcsiUpdateTask_Success) {
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;

    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    // 验证任务已启动，可以通过停止来验证
    m_link_info->stop_ncsi_update_task();
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StartNcsiUpdateTask_WithNullNcsi) {
    mc::milliseconds              interval(1000);
    uint8_t                       port_id    = 0;
    uint8_t                       package_id = 0;
    dev::ncsi_over_mctp_mellanox_ptr null_ncsi = nullptr;

    // 应该能正常处理空指针
    m_link_info->start_ncsi_update_task_mellanox(null_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    m_link_info->stop_ncsi_update_task();
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StartNcsiUpdateTask_DifferentIntervals) {
    std::vector<mc::milliseconds> intervals = {mc::milliseconds(100), mc::milliseconds(500),
                                                mc::milliseconds(1000)};
    uint8_t                        port_id    = 0;
    uint8_t                        package_id = 0;

    for (auto& interval : intervals) {
        m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        m_link_info->stop_ncsi_update_task();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StartNcsiUpdateTask_DifferentPortIds) {
    mc::milliseconds interval(1000);
    uint8_t          package_id = 0;
    std::vector<uint8_t> port_ids = {0, 1, 2, 255};

    for (auto port_id : port_ids) {
        m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        m_link_info->stop_ncsi_update_task();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StartNcsiUpdateTask_DifferentPackageIds) {
    mc::milliseconds interval(1000);
    uint8_t          port_id = 0;
    std::vector<uint8_t> package_ids = {0, 1, 2, 255};

    for (auto package_id : package_ids) {
        m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        m_link_info->stop_ncsi_update_task();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StartNcsiUpdateTask_MultipleStarts) {
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;

    // 多次启动应该能正常处理
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    m_link_info->stop_ncsi_update_task();
    EXPECT_TRUE(true);
}

// ========================= stop_ncsi_update_task 测试 =========================

TEST_F(NetworkPortLinkInfoTest, StopNcsiUpdateTask_WithoutStart) {
    // 未启动时停止应该能正常处理
    m_link_info->stop_ncsi_update_task();
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StopNcsiUpdateTask_AfterStart) {
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;

    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    m_link_info->stop_ncsi_update_task();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    EXPECT_TRUE(true);
}

TEST_F(NetworkPortLinkInfoTest, StopNcsiUpdateTask_MultipleStops) {
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;

    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    // 多次停止应该能正常处理
    m_link_info->stop_ncsi_update_task();
    m_link_info->stop_ncsi_update_task();
    m_link_info->stop_ncsi_update_task();
    EXPECT_TRUE(true);
}

// ========================= 定时器回调测试 =========================

TEST_F(NetworkPortLinkInfoTest, TimerCallback_UpdatesLinkStatus) {
    // 设置 MCTP 请求处理器
    dev::set_mctp_request_handler([](const mc::dict& ctx, const std::vector<uint8_t>& data,
                                     std::string& rsp) {
        EXPECT_EQ(ctx.at("PacketType").as<int64_t>(), GET_LINK_STATUS);
        EXPECT_TRUE(data.empty());
        // link_flag=1 (UP), speed_duplex=8 (10000Mbps, full duplex)
        rsp = make_get_link_status_response(0x01, 0x08);
        return true;
    });

    mc::milliseconds interval(100);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);

    // 等待定时器触发
    std::this_thread::sleep_for(std::chrono::milliseconds(200));

    // 验证链路状态是否被更新（由于定时器回调会调用 update_link_status_by_ncsi）
    // 注意：由于是异步操作，这里主要验证不会崩溃
    m_link_info->stop_ncsi_update_task();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    EXPECT_TRUE(true);
}

// ========================= 多实例测试 =========================

TEST_F(NetworkPortLinkInfoTest, MultiInstance) {
    std::vector<mc::shared_ptr<dev::NetworkPort_LinkInfo>> link_infos;
    for (int i = 0; i < 3; i++) {
        std::string object_name =
            "link_info_" + std::to_string(i) + "_" + std::to_string(std::time(nullptr)) + "_" +
            std::to_string(i * 1000);
        std::string position = std::to_string(i + 3000);

        auto link_info = mc::make_shared<dev::NetworkPort_LinkInfo>();

        mc::milliseconds interval(1000);
        uint8_t          port_id    = i;
        uint8_t          package_id = i;
        link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
        link_infos.push_back(link_info);
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    for (auto& link_info : link_infos) {
        if (link_info && m_test_service) {
            try {
                link_info->stop_ncsi_update_task();
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            } catch (...) {
            }
        }
    }
    link_infos.clear();
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    EXPECT_TRUE(true);
}

// ========================= 综合测试 =========================

TEST_F(NetworkPortLinkInfoTest, CompleteWorkflow) {
    // 1. 设置 MCTP 请求处理器，返回模拟的链路状态信息
    dev::set_mctp_request_handler([](const mc::dict& ctx, const std::vector<uint8_t>& data,
                                     std::string& rsp) {
        EXPECT_EQ(ctx.at("PacketType").as<int64_t>(), GET_LINK_STATUS);
        EXPECT_TRUE(data.empty());
        // link_flag=1 (UP), speed_duplex=8 (10000Mbps, full duplex)
        rsp = make_get_link_status_response(0x01, 0x08);
        return true;
    });

    // 2. 启动任务
    mc::milliseconds interval(1000);
    uint8_t          port_id    = 0;
    uint8_t          package_id = 0;
    m_link_info->start_ncsi_update_task_mellanox(m_ncsi, interval, port_id, package_id);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    // 3. 手动更新链路状态
    bool result = m_link_info->update_link_status_by_ncsi(port_id);
    EXPECT_TRUE(result);
    EXPECT_EQ(m_link_info->LinkStatus.get_value(), 1);
    EXPECT_EQ(m_link_info->SpeedMbps.get_value(), 10000);
    EXPECT_EQ(m_link_info->FullDuplex.get_value(), true);

    // 4. 停止任务
    m_link_info->stop_ncsi_update_task();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    EXPECT_TRUE(true);
}

} // namespace test
} // namespace interface
} // namespace mellanox
} // namespace pcie_nic_card
} // namespace drivers

